package model.code;

import model.code.line.SingleLine;
import model.code.line.LineList;
import observer.ListObserver;
import observer.ObservedList;

/**
 * This is mostly for use in include files to indicate function prototypes, but could be used in normal files too.
 *
 * Automatic prototyping can add prototypes at the beginning of normal files to avoid having to explicitly prototype functions,
 * but this is for functions that might be accessible outside the file in which they are defined.
 *
 * @author Neil Dickson
 */
public class Prototypes extends CodeFileChunk implements ListObserver<Function> {
	private	ObservedList<Rowable>	functionNames;
	private	ObservedList<Function>	functions;

	public Prototypes(Function[] functions) {
		super("","");
		this.functions = new ObservedList<Function>();
		this.functions.addAll(functions);
		functionNames = new ObservedList<Rowable>();
		for (int i = 0; i<functions.length; i++) {
			Function f = functions[i];
			functionNames.add(new Rowable(new SingleLine[]{new SingleLine(f.getName().toCharArray(), this, LOCATION_CONSTANTS_MEMBERS, i<<16)}));
		}
		this.functions.addObserver(this);
	}
	public Prototypes(Function function) {
		this(new Function[]{function});
	}

	public ObservedList<Function> getFunctions() {
		return functions;
	}
	public ObservedList<Rowable> getFunctionNames() {
		return functionNames;
	}

	/**
	 * NOTE: This doesn't copy correctly if the functions are copied as well and the prototypes are to refer to them.
	 * @return a new Prototypes object referring to the same functions as this
	 */
	public Prototypes copy() {
		Function[] newFunctions = new Function[functions.size()];
		for (int i=0;i< functions.size();++i) {
			newFunctions[i] = functions.get(i);
		}
		return new Prototypes(newFunctions);
	}

	public void listElementAdded(ObservedList<Function> s, int index) {
		functionNames.add(index,new Rowable(new SingleLine[]{new SingleLine(s.get(index).getName().toCharArray(), this, LOCATION_CONSTANTS_MEMBERS, index<<16)}));
		for (int i=index+1;i<functionNames.size();++i) {
			functionNames.get(i).strings[0].setLocation(this, LOCATION_CONSTANTS_MEMBERS, i<<16);
		}
	}

	public void listElementRemoved(ObservedList<Function> s, int index, Function element) {
		functionNames.remove(index);
		for (int i=index;i<functionNames.size();++i) {
			functionNames.get(i).strings[0].setLocation(this, LOCATION_CONSTANTS_MEMBERS, i<<16);
		}
	}

	public void listElementSet(ObservedList<Function> s, int index, Function oldElement) {
		functionNames.set(index,new Rowable(new SingleLine[]{new SingleLine(s.get(index).getName().toCharArray(), this, LOCATION_CONSTANTS_MEMBERS, index<<16)}));
	}

	public void listElementMoved(ObservedList<Function> s, int from, int to) {
		functionNames.move(from,to);
		functionNames.get(from).strings[0].setLocation(this, LOCATION_CONSTANTS_MEMBERS, from<<16);
		functionNames.get(to).strings[0].setLocation(this, LOCATION_CONSTANTS_MEMBERS, to<<16);
	}

	public LineList getLineList(int locType, int locIndex) {
		int row = locIndex>>>16;
		int col = locIndex&0xFFFF;
		ObservedList<? extends Rowable> table = null;
		if (locType==LOCATION_SEE_ALSO) {
			table = functionNames;
		}
		else {
			return super.getLineList(locType, locIndex);
		}
		if (table!=null && table.size()>row && table.get(row).getStrings().length>col) {
			return table.get(row).getStrings()[col];
		}
		return null;
	}
	public LineList getNextLineList(LineList list) {
		int locType = list.getLocationType();
		int locIndex = list.getLocationIndex();
		if (locType==LOCATION_NAME) {
			return description;
		}
		if (locType==LOCATION_DESCRIPTION) {
			if (functionNames.size()>0) {
				return getLineList(LOCATION_SEE_ALSO,0);
			}
			return null;
		}
		int row = locIndex>>>16;
		int col = locIndex&0xFFFF;
		if (locType==LOCATION_SEE_ALSO) {
			ObservedList<? extends Rowable> table = functionNames;
			if (col+1<table.get(row).getSize()) {
				return getLineList(locType,locIndex+1);
			}
			if (row+1<table.size()) {
				return getLineList(locType,(row+1)<<16);
			}
		}
		return null;
	}
	public LineList getPrevLineList(LineList list) {
		int locType = list.getLocationType();
		int locIndex = list.getLocationIndex();
		if (locType==LOCATION_NAME) {
			return null;
		}
		if (locType==LOCATION_DESCRIPTION) {
			return name;
		}
		int row = locIndex>>>16;
		int col = locIndex&0xFFFF;
		if (locType==LOCATION_SEE_ALSO) {
			if (col-1>=0) {
				return getLineList(locType,locIndex-1);
			}
			if (row-1>=0) {
				return getLineList(locType,(row-1)<<16);
			}
			return description;
		}
		return null;
	}

	public LineList getLastLineList() {
		if (functionNames.size()>0) {
			return functionNames.getLast().getStrings()[functionNames.getLast().getSize()-1];
		}
		return description;
	}
}